package chaoM3u8

import (
	"bufio"
	"fmt"
	"io"
	"net/http"
	"path"
	"strconv"
	"strings"

	"github.com/bzsome/ChaoGom3u8/utils"
	"github.com/bzsome/chaoGo/workpool"
)

type M3u8Down struct {
	Url       string //m3u8文件url
	SaveDir   string //合成后文件保存路径
	WpSize    int    //同时下载文件的数量
	PoolSize  int    //单文件线程池大小
	ChuckSize int64  //单文件线程下载块大小

	tempDir  string             //临时文件存储地址(M3u8视频段)
	saveFile string             //保存绝对文件名
	m3u8     M3u8               //m3u8对象
	wp       *workpool.WorkPool //线程池
}

/*从服务商获得m3u8和ts信息*/
func (m3u8Down *M3u8Down) initM3u8AndTs() error {
	end := strings.LastIndex(m3u8Down.Url, "/") + 1
	m3u8Down.m3u8 = M3u8{
		BaseUrl:     m3u8Down.Url[0:end],
		FileName:    m3u8Down.Url[end:],
		DownloadUrl: m3u8Down.Url,
		tsList:      make([]M3u8Ts, 0),
	}

	res, err := http.Get(m3u8Down.Url)
	if err != nil {
		return err
	}

	buf := bufio.NewReader(res.Body)
	for {
		line, err := buf.ReadString('\n')
		line = strings.TrimSpace(line)
		if strings.Index(line, "#EXTINF:") == 0 {
			ts := M3u8Ts{}
			ts.Seconds, _ = strconv.ParseFloat(line[8:], 64)

			//文件名在下一行中
			line, _ := buf.ReadString('\n')
			ts.FileName = strings.TrimSpace(line)
			ts.DownloadUrl = m3u8Down.m3u8.BaseUrl + ts.FileName

			m3u8Down.m3u8.tsList = append(m3u8Down.m3u8.tsList, ts)
		}
		if err != nil {
			if err == io.EOF {
				break
			} else {
				return err
			}
		}
	}
	return nil
}

func (m3u8Down *M3u8Down) Down() {
	m3u8Down.tempDir = path.Join(m3u8Down.SaveDir, "temp", utils.FilenameByUri(m3u8Down.Url))
	utils.CreateDir(m3u8Down.SaveDir)
	utils.CreateDir(m3u8Down.tempDir)

	m3u8Down.initM3u8AndTs()
	m3u8Down.saveFile = path.Join(m3u8Down.SaveDir, m3u8Down.m3u8.FileName+".mp4")

	//使用线程池来创建下载任务(创建任务耗费时间)
	m3u8Down.wp = workpool.New(m3u8Down.WpSize)

	//开始下载视频段
	for _, ts := range m3u8Down.m3u8.tsList {
		//TODO 循环体本想使用go协程异步。但是导致Downloader.Resolve().httpClient.Do()异常退出，未找到原因
		doneOneTs := m3u8Down.doneOneTs(ts)
		m3u8Down.wp.Do(doneOneTs)
	}

	m3u8Down.wp.Wait()

	//下载已完成，合并视频文件
	fileArr := make([]string, 0)
	for _, ts := range m3u8Down.m3u8.tsList {
		filePath := path.Join(m3u8Down.tempDir, ts.FileName)
		fileArr = append(fileArr, filePath)
	}

	utils.MergeFile(fileArr, m3u8Down.saveFile)
	fmt.Printf("视频合并完成：%s\n", m3u8Down.saveFile)
}

//创建下载ts文件的方法，供线程池调用
func (m3u8Down *M3u8Down) doneOneTs(ts M3u8Ts) func() error {
	return func() error {
		downloader, err := utils.DownChuckUrl(ts.DownloadUrl, m3u8Down.tempDir, ts.FileName, m3u8Down.PoolSize, m3u8Down.ChuckSize)
		if err != nil {
			//线程池的bug，返回err将导致线程池停止
			fmt.Println(err)
		} else {
			if !downloader.Wait {
				downloader.WaitDone()
			}
		}
		return nil
	}
}

func (down *M3u8Down) GetSavePath() string {
	return down.saveFile
}
